package com.gopay.cashier.web.utils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.security.Key;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.Security;

import javax.crypto.Cipher;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMReader;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.FileSystemResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;

/**
 * 移动支付js加密java解密，参考common下的RSAUtils
 * @author lizheng
 *
 */
public class RSAUtil4js {
    
	protected final static Log logger = LogFactory.getLog(RSAUtil4js.class);

	private RSAUtil4js() {
	}

	private static final String RSA = "RSA";

	private static final String PROVIDER = "BC";

	private static final String FILE_NAME = "classpath:RSAPrivate4js.pem";
//	private static final String FILE_NAME = "classpath:privatekey.pem";

	private static BouncyCastleProvider BOUNCY_CASTLE_PROVIDER =new org.bouncycastle.jce.provider.BouncyCastleProvider();
	
	private static Object decryptRSAObj;

	static {
		Security.addProvider(BOUNCY_CASTLE_PROVIDER);
		try {
		    byte[] privateKeyData = readFile(getCanonicalFilePath(FILE_NAME));
            String strData = new String(privateKeyData);
            decryptRSAObj = string2Key(strData);
        } catch (Exception e) {
            logger.error("read private key file data error!!!");
            e.printStackTrace();
        }
	}

	public static Object string2Key(String pemstr) {
		PEMReader reader = new PEMReader(new StringReader(pemstr));
		Object pkio = null;
		try {
			pkio = reader.readObject();
		} catch (IOException e) {
			logger.error("PEMReader readObject exception!" + pemstr, e);
		} finally {
			try {
				reader.close();
			} catch (IOException e) {
				logger.error("PEMReader close exception!" + pemstr, e);
			}
		}
		return pkio;
	}

	/**
	 * readFile.
	 * 
	 * @param filename
	 *            filename
	 * @return
	 * @throws Exception .
	 * @return byte[]
	 */
	public static byte[] readFile(String filename) throws Exception {
		InputStream is = new FileInputStream(filename);
		byte[] ret = new byte[is.available()];
		is.read(ret);
		is.close();
		return ret;
	}

	/**
	 * 解密.
	 * 
	 * @param key
	 *            解密的密钥
	 * @param raw
	 *            已经加密的数据
	 * @return 解密后的明文
	 * @throws Exception .
	 */
	public static byte[] decrypt(Key key, byte[] raw) throws Exception {
		try {
			Cipher cipher = Cipher.getInstance(RSA, BOUNCY_CASTLE_PROVIDER);
			// Cipher cipher = Cipher.getInstance(RSA, PROVIDER);
			cipher.init(Cipher.DECRYPT_MODE, key);
			int blockSize = cipher.getBlockSize();
			ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
			int j = 0;

			while (raw.length - j * blockSize > 0) {
				bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
				j++;
			}
			return bout.toByteArray();
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}

	/**
	 * 加密.
	 * 
	 * @param key
	 *            加密的密钥
	 * @param data
	 *            待加密的明文数据
	 * @return 加密后的数据
	 * @throws Exception .
	 */
	public static byte[] encrypt(Key key, byte[] data) throws Exception {
		try {
			Cipher cipher = Cipher.getInstance(RSA, PROVIDER);
			cipher.init(Cipher.ENCRYPT_MODE, key);
			int blockSize = cipher.getBlockSize();// 获得加密块大小，如：加密前数据为128个byte，而key_size=1024
													// 加密块大小为127
													// byte,加密后为128个byte;因此共有2个加密块，第一个127
													// byte第二个为1个byte
			int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
			int leavedSize = data.length % blockSize;
			int blocksSize = leavedSize != 0 ? data.length / blockSize + 1 : data.length / blockSize;
			byte[] raw = new byte[outputSize * blocksSize];
			int i = 0;
			while (data.length - i * blockSize > 0) {
				if (data.length - i * blockSize > blockSize) {
					cipher.doFinal(data, i * blockSize, blockSize, raw, i * outputSize);
				} else {
					cipher.doFinal(data, i * blockSize, data.length - i * blockSize, raw, i * outputSize);
					// 这里面doUpdate方法不可用，查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到ByteArrayOutputStream中，而最后doFinal的时候才将所有的byte[]进行加密，可是到了此时加密块大小很可能已经超出了OutputSize所以只好用dofinal方法。
				}

				i++;
			}
			return raw;
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}

	/**
	 * hex2byte.
	 * 
	 * @param hexStr
	 *            hexStr
	 * @return byte[]
	 */
	public static byte[] hex2byte(String hexStr) {
		byte[] bts = new byte[hexStr.length() / 2];
		for (int i = 0, j = 0; j < bts.length; j++) {
			bts[j] = (byte) Integer.parseInt(hexStr.substring(i, i + 2), 16);
			i += 2;
		}
		return bts;
	}

	/**
	 * byte数组转换成16进制字符串.
	 * 
	 * @param b
	 *            b
	 * @return String
	 */
	public static String byte2hex(byte[] b) {
		StringBuffer result = new StringBuffer();
		for (int i = 0; i < b.length; i++) {
			String hex = Integer.toHexString(b[i] & 0xFF);
			if (hex.length() == 1) {
				hex = '0' + hex;
			}
			result.append(hex.toLowerCase());
		}

		return result.toString();
	}

	/**
	 * 对RSA加密的密文进行解密.
	 * 
	 * @param hexStr
	 *            hexStr
	 * @return String
	 * @throws Exception .
	 */
	public static String decryptRSA(String hexStr) throws Exception {
		PrivateKey privateKey = null;
		String ret = null;

		if (decryptRSAObj instanceof KeyPair) {
			privateKey = ((KeyPair) decryptRSAObj).getPrivate();
			byte[] srcData = decrypt(privateKey, hex2byte(hexStr));
			ret = new String(srcData);
		}

		return ret;
	}

	public static String getCanonicalFilePath(String filePath) {
		try {
			ResourceLoader resourceLoader = new DefaultResourceLoader();
			Resource resource = resourceLoader.getResource(filePath);
			File file = null;
			if (resource.exists()) {
				file = resource.getFile();
			} else {
				resourceLoader = new FileSystemResourceLoader();
				resource = resourceLoader.getResource(filePath);
				if (resource.exists()) {
					file = resource.getFile();
				} else {
					return filePath;
				}
			}
			if (file != null && file.exists() && file.isFile()) {
				String absFilePath = file.getCanonicalPath();
				return absFilePath;
			} else {
				return filePath;
			}
		} catch (Exception e) {
			return filePath;
		}
	}
	public static KeyPair getKeyPair()throws Exception{  
//		FileInputStream fis= new FileInputStream("C:\\RSAKey.txt");
//		 ObjectInputStream oos = new ObjectInputStream(fis);
//		 KeyPair kp= (KeyPair) oos.readObject();
//		 oos.close();
//		 fis.close();
//		 return kp;
         return (KeyPair) decryptRSAObj;  
    }  
//	public static void main(String[] args) throws Exception {  
//        String test = "hello world";  
//        byte[] en_test = encrypt(getKeyPair().getPublic(),test.getBytes());  
//        byte[] de_test = decrypt(getKeyPair().getPrivate(),en_test);  
//        System.out.println(new String(de_test));  
//    }
}
